"""Code Compilation Module"""

import logging
import os
from typing import Dict, Optional, Any, List, Tuple
from .ssh_manager import SSHManager

logger = logging.getLogger(__name__)


class CompilerManager:
    """Manages code compilation on remote servers"""
    
    SUPPORTED_COMPILERS = ['gcc', 'hipcc', 'python']
    
    def __init__(self, ssh_manager: SSHManager):
        self.ssh_manager = ssh_manager
    
    def compile_code(self, connection_id: str, source_file: str, compiler: str,
                     output_file: Optional[str] = None, compile_flags: Optional[str] = None,
                     working_dir: str = "~") -> Dict[str, Any]:
        """Compile source code on remote server
        
        Args:
            connection_id: SSH connection identifier
            source_file: Path to source file on remote server
            compiler: Compiler to use (gcc, hipcc, python)
            output_file: Output executable name (auto-generated if None)
            compile_flags: Additional compiler flags
            working_dir: Working directory for compilation
            
        Returns:
            Compilation result dictionary
            
        Raises:
            ValueError: If compiler not supported
            Exception: If compilation fails
        """
        if compiler not in self.SUPPORTED_COMPILERS:
            raise ValueError(f"Unsupported compiler: {compiler}. Supported: {self.SUPPORTED_COMPILERS}")
        
        try:
            # Verify source file exists
            exit_code, stdout, stderr = self.ssh_manager.execute_command(
                connection_id, f"test -f {source_file} && echo 'exists'", working_dir=working_dir
            )
            
            if exit_code != 0 or "exists" not in stdout:
                raise Exception(f"Source file not found: {source_file}")
            
            # Generate output file name if not provided
            if not output_file:
                base_name = os.path.splitext(os.path.basename(source_file))[0]
                if compiler == 'python':
                    output_file = f"{base_name}.pyc"
                else:
                    output_file = base_name
            
            # Build compilation command
            compile_command = self._build_compile_command(
                compiler, source_file, output_file, compile_flags
            )
            
            logger.info(f"Compiling {source_file} with {compiler} on {connection_id}")
            
            # Execute compilation
            exit_code, stdout, stderr = self.ssh_manager.execute_command(
                connection_id, compile_command, working_dir=working_dir
            )
            
            # Check compilation result
            compilation_result = {
                'source_file': source_file,
                'compiler': compiler,
                'output_file': output_file,
                'compile_command': compile_command,
                'exit_code': exit_code,
                'stdout': stdout,
                'stderr': stderr,
                'success': exit_code == 0,
                'working_dir': working_dir
            }
            
            if exit_code == 0:
                # Verify output file was created (except for Python compilation)
                if compiler != 'python':
                    verify_exit, verify_out, verify_err = self.ssh_manager.execute_command(
                        connection_id, f"test -f {output_file} && echo 'created'", working_dir=working_dir
                    )
                    compilation_result['output_exists'] = "created" in verify_out
                else:
                    compilation_result['output_exists'] = True
                
                logger.info(f"Successfully compiled {source_file} to {output_file}")
            else:
                logger.error(f"Compilation failed for {source_file}: {stderr}")
                compilation_result['output_exists'] = False
            
            return compilation_result
            
        except Exception as e:
            logger.error(f"Error compiling code: {str(e)}")
            raise Exception(f"Compilation failed: {str(e)}")
    
    def execute_program(self, connection_id: str, program_path: str,
                       arguments: Optional[str] = None, working_dir: str = "~") -> Dict[str, Any]:
        """Execute a compiled program or Python script
        
        Args:
            connection_id: SSH connection identifier
            program_path: Path to executable or Python script
            arguments: Command line arguments
            working_dir: Working directory for execution
            
        Returns:
            Execution result dictionary
            
        Raises:
            Exception: If execution fails
        """
        try:
            # Check if file exists and determine execution method
            exit_code, stdout, stderr = self.ssh_manager.execute_command(
                connection_id, f"test -f {program_path} && echo 'exists'", working_dir=working_dir
            )
            
            if exit_code != 0 or "exists" not in stdout:
                raise Exception(f"Program file not found: {program_path}")
            
            # Determine execution command based on file type
            execution_command = self._build_execution_command(program_path, arguments)
            
            logger.info(f"Executing {program_path} on {connection_id}")
            
            # Execute program
            exit_code, stdout, stderr = self.ssh_manager.execute_command(
                connection_id, execution_command, working_dir=working_dir
            )
            
            execution_result = {
                'program_path': program_path,
                'arguments': arguments or '',
                'execution_command': execution_command,
                'exit_code': exit_code,
                'stdout': stdout,
                'stderr': stderr,
                'success': exit_code == 0,
                'working_dir': working_dir
            }
            
            if exit_code == 0:
                logger.info(f"Successfully executed {program_path}")
            else:
                logger.warning(f"Program {program_path} exited with code {exit_code}")
            
            return execution_result
            
        except Exception as e:
            logger.error(f"Error executing program: {str(e)}")
            raise Exception(f"Program execution failed: {str(e)}")
    
    def get_compiler_info(self, connection_id: str, compiler: str) -> Dict[str, Any]:
        """Get compiler version and information
        
        Args:
            connection_id: SSH connection identifier
            compiler: Compiler name
            
        Returns:
            Compiler information dictionary
        """
        try:
            if compiler == 'gcc':
                cmd = "gcc --version"
            elif compiler == 'hipcc':
                cmd = "hipcc --version"
            elif compiler == 'python':
                cmd = "python3 --version"
            else:
                raise ValueError(f"Unknown compiler: {compiler}")
            
            exit_code, stdout, stderr = self.ssh_manager.execute_command(
                connection_id, cmd, working_dir="~"
            )
            
            return {
                'compiler': compiler,
                'available': exit_code == 0,
                'version_output': stdout if exit_code == 0 else stderr,
                'error': stderr if exit_code != 0 else None
            }
            
        except Exception as e:
            return {
                'compiler': compiler,
                'available': False,
                'version_output': '',
                'error': str(e)
            }
    
    def list_available_compilers(self, connection_id: str) -> List[Dict[str, Any]]:
        """List all available compilers on remote server
        
        Args:
            connection_id: SSH connection identifier
            
        Returns:
            List of compiler information dictionaries
        """
        compilers = []
        for compiler in self.SUPPORTED_COMPILERS:
            compiler_info = self.get_compiler_info(connection_id, compiler)
            compilers.append(compiler_info)
        
        available_count = sum(1 for c in compilers if c['available'])
        logger.info(f"Found {available_count}/{len(self.SUPPORTED_COMPILERS)} available compilers on {connection_id}")
        
        return compilers
    
    def _build_compile_command(self, compiler: str, source_file: str,
                              output_file: str, compile_flags: Optional[str] = None) -> str:
        """Build compilation command string
        
        Args:
            compiler: Compiler name
            source_file: Source file path
            output_file: Output file path
            compile_flags: Additional compilation flags
            
        Returns:
            Complete compilation command
        """
        flags = compile_flags or ""
        
        if compiler == 'gcc':
            return f"gcc {flags} -o {output_file} {source_file}"
        elif compiler == 'hipcc':
            return f"hipcc {flags} -o {output_file} {source_file}"
        elif compiler == 'python':
            # Python compilation using py_compile
            return f"python3 -m py_compile {source_file}"
        else:
            raise ValueError(f"Unknown compiler: {compiler}")
    
    def _build_execution_command(self, program_path: str, arguments: Optional[str] = None) -> str:
        """Build execution command string
        
        Args:
            program_path: Path to executable or script
            arguments: Command line arguments
            
        Returns:
            Complete execution command
        """
        args = arguments or ""
        
        # Check if it's a Python script
        if program_path.endswith('.py'):
            return f"python3 {program_path} {args}".strip()
        else:
            # Assume it's a compiled executable
            # Make sure it's executable
            return f"chmod +x {program_path} && ./{program_path} {args}".strip()
    
    def create_sample_code(self, connection_id: str, language: str, 
                          filename: Optional[str] = None, working_dir: str = "~") -> str:
        """Create sample code file for testing
        
        Args:
            connection_id: SSH connection identifier
            language: Programming language (c, cpp, python)
            filename: Output filename (auto-generated if None)
            working_dir: Working directory
            
        Returns:
            Path to created file
            
        Raises:
            ValueError: If language not supported
        """
        if not filename:
            if language in ['c', 'gcc']:
                filename = "hello.c"
            elif language in ['cpp', 'hipcc']:
                filename = "hello.cpp"
            elif language == 'python':
                filename = "hello.py"
            else:
                raise ValueError(f"Unsupported language: {language}")
        
        # Generate sample code content
        if language in ['c', 'gcc']:
            code_content = '''#include <stdio.h>

int main() {
    printf("Hello, World from C!\\n");
    return 0;
}
'''
        elif language in ['cpp', 'hipcc']:
            code_content = '''#include <iostream>

int main() {
    std::cout << "Hello, World from C++!" << std::endl;
    return 0;
}
'''
        elif language == 'python':
            code_content = '''#!/usr/bin/env python3

def main():
    print("Hello, World from Python!")

if __name__ == "__main__":
    main()
'''
        else:
            raise ValueError(f"Unsupported language: {language}")
        
        try:
            # Create file on remote server
            client = self.ssh_manager.get_client(connection_id)
            stdin, stdout, stderr = client.exec_command(f"cd {working_dir} && cat > {filename}")
            stdin.write(code_content)
            stdin.close()
            
            # Wait for completion
            exit_code = stdout.channel.recv_exit_status()
            
            if exit_code == 0:
                logger.info(f"Created sample {language} code file: {filename}")
                return filename
            else:
                raise Exception(f"Failed to create file: {stderr.read().decode()}")
                
        except Exception as e:
            logger.error(f"Error creating sample code: {str(e)}")
            raise Exception(f"Sample code creation failed: {str(e)}")
